/*
 * @Author: fuhongliang-TSJ hongliang.fu@4d-bios.com
 * @Date: 2025-10-13 10:59:09
 * @LastEditTime: 2025-10-15 18:32:40
 * @LastEditors: fuhongliang-TSJ hongliang.fu@4d-bios.com
 * @Description: 
 * @FilePath: \lc_-esp32_-s3\main\lc\handheld\echo_app.c
 * 立创实战派ESP32-S3开发板
 * Copyright (c) 2025 by 4D Bios, All Rights Reserved. 
 */

#include "handheld.h"
#include "format_wav.h"
#include "sd_card.h"
#include "audio.h"
#include "pca9557.h"
#include "bsp_lvgl.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#define RECORD_TIME_SEC    (10)
#define RECORD_FILE_PATH   "/mp3/RECORD.WAV"

#define EXAMPLE_I2S_CHAN_NUM       (2)
#define EXAMPLE_I2S_SAMPLE_RATE    (16000)
#define EXAMPLE_I2S_SAMPLE_BITS    (I2S_DATA_BIT_WIDTH_16BIT)

static const char *TAG = "echo_app";

static const char err_reason[][30] = {"input param is invalid",
                                      "operation timeout"
                                     };

extern lv_obj_t *icon_in_obj; // 应用界面
extern int icon_flag;

#define RECV_BUF_SIZE   2400

LV_FONT_DECLARE(font_alipuhui20);

/**
 * @description: 返回主界面向上滑动事件处理函数
 * @param {lv_event_t} *e
 * @return {*}
 */
static void my_gesture_event_cb(lv_event_t *e)
{
    lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_get_act());
    if (dir == LV_DIR_TOP)
    {
        lv_obj_del(icon_in_obj); //删除画布
        //lv_timer_del(my_lv_timer);
        icon_flag = 0;
    }
}

static esp_err_t record_wav(void)
{
    esp_err_t ret = ESP_OK;

    uint32_t byte_rate = EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_CHAN_NUM * EXAMPLE_I2S_SAMPLE_BITS / 8;
    uint32_t wav_size = byte_rate * RECORD_TIME_SEC;

    const wav_header_t wav_header =
        WAV_HEADER_PCM_DEFAULT(wav_size, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_CHAN_NUM);

    ESP_LOGI(TAG, "Opening file %s", RECORD_FILE_PATH);
    FILE *f = fopen(MOUNT_POINT RECORD_FILE_PATH, "w");
    ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");

    /* Write wav header */
    ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL, err,
                      TAG, "error while writing wav header");

    /* Start recording */
    size_t wav_written = 0;
        static int16_t i2s_readraw_buff[4096];
    while (wav_written < wav_size) {
        if(wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
            ESP_LOGI(TAG, "Recording: %"PRIu32"/%ds", wav_written/byte_rate + 1, RECORD_TIME_SEC);
        }
        size_t bytes_read = 0;
        size_t bytes_write = 0;
        /* Read RAW samples from ES7210 */
         bsp_i2s_read(i2s_readraw_buff, sizeof(i2s_readraw_buff), &bytes_read, pdMS_TO_TICKS(1000));
         bsp_i2s_write(i2s_readraw_buff, sizeof(i2s_readraw_buff), &bytes_write, pdMS_TO_TICKS(1000));
        /* Write the samples to the WAV file */
        ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
                          TAG, "error while writing samples to wav file");
        wav_written += bytes_read;
    }

err:
    ESP_LOGI(TAG, "Recording done! Flushing file buffer");
    fclose(f);

    return ret;
}

/**
 * @description: 回声任务
 * @param {void} *arg
 * @return {*}
 */
static void echo_task(void *arg)
{
    int16_t *mic_data = malloc(RECV_BUF_SIZE);
    if (!mic_data) {
        ESP_LOGE(TAG, "[echo] No memory for read data buffer");
        abort();
    }
    esp_err_t ret = ESP_OK;
    size_t bytes_read = 0;
    size_t bytes_write = 0;
    ESP_LOGI(TAG, "[echo] Echo start");

    bsp_codec_set_fs(EXAMPLE_I2S_SAMPLE_RATE, 16, 2);
    bsp_codec_volume_set(60, NULL);

    pa_en(1);  // 打开音频功放

    while (1) {
        memset(mic_data, 0, RECV_BUF_SIZE);

        /* Read sample data from mic */
        ret =  bsp_i2s_read(mic_data, RECV_BUF_SIZE, &bytes_read, pdMS_TO_TICKS(1000));
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "[echo] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
            abort();
        }
        
        /* Write sample data to earphone */
        ret = bsp_i2s_write(mic_data, RECV_BUF_SIZE, &bytes_write, pdMS_TO_TICKS(1000));
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "[echo] i2s write failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
            abort();
        }

        if (bytes_read != bytes_write) {
            ESP_LOGW(TAG, "[echo] %d bytes read but only %d bytes are written", bytes_read, bytes_write);
        }
        if (icon_flag == 0) break;

        #if 0     /* 存储录音到SD卡，用music应用可以打开该录音 */  
        /* 挂载SD卡 */
        ret = sd_init();
        if (ret != ESP_OK) {
            return;
        }

        /* 录音 */
        esp_err_t err = record_wav();
        if(err == ESP_OK) {
            ESP_LOGI(TAG, "Audio was successfully recorded into "RECORD_FILE_PATH
                        ". You can now remove the SD card safely");
        } else {
            ESP_LOGE(TAG, "Record failed, "RECORD_FILE_PATH" on SD card may not be playable.");
        }

        /* 弹出SD卡 */
        ret = sd_unmount();
        if (ret != ESP_OK) {
            return;
        }

        break;
        #endif
    }
    
    free(mic_data);
    pa_en(0);  // 关闭音频功放
    vTaskDelete(NULL);
}

/**
 * @description: 回声器应用
 * @return {*}
 */
void echo_app(void)
{
    lvgl_port_lock(0);

    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_radius(&style, 10);
    lv_style_set_opa(&style, LV_OPA_COVER);
    lv_style_set_bg_color(&style, lv_color_hex(0x00BFFF));
    lv_style_set_bg_grad_color(&style, lv_color_hex(0x00BF00));
    lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
    lv_style_set_border_width(&style, 0);
    lv_style_set_pad_all(&style, 0);
    lv_style_set_width(&style, 320);
    lv_style_set_height(&style, 240);
    
    // 创建一个界面
    icon_in_obj = lv_obj_create(lv_scr_act());
    lv_obj_add_style(icon_in_obj, &style, 0);

    // 提示文本
    lv_obj_t *label_text = lv_label_create(icon_in_obj);
    lv_label_set_text(label_text, "请对准USB旁边的麦克风说话");
    lv_obj_set_style_text_color(label_text, lv_color_hex(0xffffff), 0);
    lv_obj_set_style_text_font(label_text, &font_alipuhui20, 0);
    lv_obj_center(label_text);

    // 返回提示符
    lv_obj_t *label_back = lv_label_create(icon_in_obj);
    lv_label_set_text(label_back, LV_SYMBOL_UP);
    lv_obj_set_style_text_color(label_back, lv_color_hex(0xffffff), 0);
    lv_obj_set_style_text_font(label_back, &lv_font_montserrat_20, 0);
    lv_obj_align(label_back, LV_ALIGN_BOTTOM_RIGHT, -20, -20);

    // 添加向上滑动退出功能
    lv_obj_add_event_cb(icon_in_obj, my_gesture_event_cb, LV_EVENT_GESTURE, 0);
    lv_obj_clear_flag(icon_in_obj, LV_OBJ_FLAG_GESTURE_BUBBLE);
    lv_obj_add_flag(icon_in_obj, LV_OBJ_FLAG_CLICKABLE);

    lvgl_port_unlock();

    icon_flag = 9;  //标记已经进入第9个图标 

    // 创建一个音乐回声的任务
    xTaskCreatePinnedToCore(echo_task, "echo_task", 8 * 1024, NULL, 5, NULL, 1);
}